const http = require('http')
const { performance } = require('perf_hooks')

function requestOnce(url, opts={}) {
  return new Promise((resolve, reject) => {
    const req = http.get(url, opts, (res) => {
      // consume body
      res.on('data', ()=>{})
      res.on('end', ()=> resolve(res.statusCode))
    })
    req.on('error', reject)
  })
}

async function runBench(url, total=100, concurrency=10) {
  const start = performance.now()
  let inflight = 0
  let completed = 0
  let errors = 0

  function nextBatch() {
    const promises = []
    for (let i = 0; i < concurrency; i++) {
      if (completed + inflight >= total) break
      inflight++
      const p = requestOnce(url, requestOptions).then(() => { completed++; inflight-- }).catch(()=>{ errors++; inflight--; })
      promises.push(p)
    }
    return Promise.all(promises)
  }

  while (completed < total) {
    await nextBatch()
  }

  const end = performance.now()
  console.log(`URL: ${url} total: ${total} concurrency: ${concurrency} duration_ms: ${Math.round(end-start)} errors: ${errors}`)
}

// usage: node bench.js http://127.0.0.1:8080/api/hello 1000 50
const args = process.argv.slice(2)
if (args.length < 1) {
  console.log('Usage: node bench.js <url> [total] [concurrency]')
  process.exit(1)
}
const url = args[0]
const total = parseInt(args[1] || '100')
const concurrency = parseInt(args[2] || '10')
// optional fourth arg: if 'close' is provided, add Connection: close header
const useConnClose = (args[3] === 'close')
const requestOptions = useConnClose ? { headers: { Connection: 'close' } } : {}
runBench(url, total, concurrency).catch(e => { console.error(e); process.exit(2) })
